package cn.com.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AssignableTypeFilter;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 类扫描工具，提供通过 Spring 和文件系统两种方式扫描指定包下的所有类。
 * 使用 Spring 的 ClassPathScanningCandidateComponentProvider 进行类扫描，同时提供文件系统方式的扫描。
 *
 * @author LiuGuodong
 * @date 2024/01/23
 */
@Slf4j
public class ClassScannerUtil {

    /**
     * 使用 Spring 的 ClassPathScanningCandidateComponentProvider 进行类扫描
     */
    private static final ClassPathScanningCandidateComponentProvider SCANNER = new ClassPathScanningCandidateComponentProvider(false);

    /**
     * 扫描指定包下的所有类（Spring 方式）。
     *
     * @param basePackage 扫描的包名
     * @return 类列表
     */
    public static List<Class> scanClassesSpring(String basePackage) {
        List<Class> classes = new ArrayList<>();

        // 添加过滤器，可以根据需要添加其他过滤器
        SCANNER.addIncludeFilter(new AssignableTypeFilter(Object.class));

        // 扫描指定包下的类
        for (org.springframework.beans.factory.config.BeanDefinition beanDefinition : SCANNER.findCandidateComponents(basePackage)) {
            try {
                // 获取类的实际类型并添加到列表中
                Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
                classes.add(clazz);
            } catch (ClassNotFoundException e) {
                log.error("类扫描失败：{}", e.getMessage());
            }
        }

        return classes;
    }

    /**
     * 扫描指定包下的所有类（文件系统方式）。
     *
     * @param packageName 扫描的包名
     * @return 类列表
     */
    public static List<Class> scanClassesFile(String packageName) {
        List<Class> classes = new ArrayList<>();

        // 将包名转换为文件系统路径
        String path = packageName.replace('.', '/');
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        URL resource = classLoader.getResource(path);

        // 如果资源为空，返回空列表
        if (resource == null) {
            return new ArrayList<>();
        }

        // 获取文件系统中的目录
        File directory = new File(PathDecoderUtil.decodePath(resource.getFile()));

        if (directory.exists()) {
            File[] files = directory.listFiles();

            // 遍历目录下的文件和子目录
            for (File file : Objects.requireNonNull(files)) {
                if (file.isDirectory()) {
                    // 递归扫描子目录
                    classes.addAll(scanClassesFile(packageName + "." + file.getName()));
                } else if (file.getName().endsWith(".class")) {
                    // 处理以 .class 结尾的文件，获取类名并加载类
                    String className = packageName + "." + file.getName().substring(0, file.getName().length() - 6);
                    try {
                        Class<?> clazz = Class.forName(className);
                        classes.add(clazz);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        return classes;
    }

    public static List<Class> scanClasses(String packageName, final Class aclss) {
        List<Class> result = scanClassesFile(packageName);
        result = result.stream().filter(e -> (e.getAnnotation(aclss) != null)).collect(Collectors.toList());
        return result;
    }
}
